package com.gitee.wsl.math.interpolator


import com.gitee.wsl.mathematics.interpolator.Interpolate
import com.gitee.wsl.mathematics.interpolator.UnInterpolatorFun
import com.gitee.wsl.mathematics.interpolator.Percent
import com.gitee.wsl.mathematics.interpolator.pct
import kotlin.math.floor
import kotlin.math.round

/**
 * http://alvyray.com/Memos/CG/Pixar/spline77.pdf
 */
 fun Interpolate.Companion.computeSpline(t1: Double, v0: Int, v1: Int, v2: Int, v3: Int): Double {
    val t2 = t1 * t1
    val t3 = t2 * t1
    return ((1 - 3 * t1 + 3 * t2 - t3) * v0
            + (4 - 6 * t2 + 3 * t3) * v1
            + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2
            + t3 * v3) / 6
}

/**
 * uniform nonrational B-spline interpolation
 */
 fun Interpolate.Companion.basis(values: List<Int>): Interpolate<Double> {
    val n = values.size - 1
    return Interpolate {percent->

        val currentIndex: Int = floor(percent * n).toInt().coerceIn(0..<n)

        val v1 = values[currentIndex]
        val v2 = values[currentIndex + 1]
        val v0 = if (currentIndex > 0) values[currentIndex - 1] else 2 * v1 - v2
        val v3 = if (currentIndex < n - 1) values[currentIndex + 2] else 2 * v2 - v1

        computeSpline((percent.coerceToDefault().value - currentIndex.toDouble() / n) * n, v0, v1, v2, v3)
    }
}

/**
 * uniform nonrational cyclical B-spline interpolation
 */
 fun Interpolate.Companion.basisClosed(values: List<Int>): Interpolate<Double> {
    val n = values.size
    return Interpolate {percent->

        val newT = if (percent.value < 0) percent.value % 1 else (percent.value % 1 + 1)
        val currentIndex = floor(newT * n).toInt()

        val v0 = values[(currentIndex + n - 1) % n]
        val v1 = values[currentIndex % n]
        val v2 = values[(currentIndex + 1) % n]
        val v3 = values[(currentIndex + 2) % n]

        computeSpline((newT - currentIndex / n) * n, v0, v1, v2, v3)
    }
}

fun Interpolate.Companion.interpolateNumber(start: Double, end: Double): Interpolate<Double> {
    val diff = end - start
    return Interpolate {percent-> start + percent.value * diff }
}


fun Interpolate.Companion.interpolateRound(start: Double, end: Double): Interpolate<Double> {
    val diff = end - start
    return Interpolate {percent-> round(start + percent.value * diff) }
}

fun Interpolate.Companion.uninterpolateNumber(start: Double, end: Double): UnInterpolatorFun<Double> {
    val diff = end - start
    return if (diff != .0) { double -> Percent((double - start) / diff) }  else { _ -> 0.pct }
}